﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GameEngine.Managers.IManagers;
using Microsoft.Xna.Framework;
using GameEngine.Scenes;
using GameEngine.Entities;
using GameEngine.EntityComponents;
using Microsoft.Xna.Framework.Graphics;

namespace GameEngine.Managers.ConcreteManagers
{
    public class PhysicsManager : IPhysicsManager
    {
        private Game game;
        private ISceneManager sceneManager;
        private static float UNIGRAVITY = 4.82f;
        private static float gameLoopTime = 1.0f;
        public static bool isBottom = false;
        public static bool isLeft = false;
        public static bool isRight = false;
        public static bool isTop = false;

        private static int collisionBoxExtraSize1 = 6; // how much extra to check the collision
        private static int collisionBoxExtraSize2 = 5;

        public PhysicsManager(Game game, ISceneManager sceneManager)
        {
            this.game = game;
            this.sceneManager = sceneManager;
        }

        public void Update(GameTime gameTime, Scene scene)
        {
            List<AbstractEntity> entities = scene.GetSceneObjects();
            
            for (int i = 0; i < entities.Count; i++)
            {
                
                CheckCollisionMap(entities[i], scene.CollisionMap);
                
                if (scene.DeathTrapMap != null)
                    CheckTrapMap(entities[i], scene.DeathTrapMap);
                for (int j = i + 1; j < entities.Count; j++)
                {
                    if (entities[i].BoundingRectangle.Intersects(entities[j].BoundingRectangle)) {
                        if (entities[i].GetComponent(typeof(CollidableComponent)) != null &&
                            entities[j].GetComponent(typeof(CollidableComponent)) != null)
                        {
                            if (PixelPerfectCollision(entities[i], entities[j]))
                            {
                                entities[i].CollisionWith(entities[j], false, false);
                                entities[j].CollisionWith(entities[i], false, false);
                            }
                            //if (IsIntersectingPixels(entities[i], entities[j], 0, 0))
                            //{
                            //    entities[i].CollisionWith(entities[j], false, false);
                            //    entities[j].CollisionWith(entities[i], false, false);
                            //}
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Checks the trap map to see if a entity that can be killed as collided with a dangerZone.
        /// </summary>
        /// <param name="entity">The entity.</param>
        /// <param name="deathTrapMap">The death trap map.</param>
        private void CheckTrapMap(AbstractEntity entity, Texture2D deathTrapMap)
        {
            TerminateableComponent killable = (TerminateableComponent)entity.GetComponent(typeof(TerminateableComponent));
            //SoundableComponent sound = (SoundableComponent)entity.GetComponent(typeof(SoundableComponent));
            if (killable != null)
            {
                Color color = new Color(0, 255, 36);
                Rectangle entityRect = ((AbstractDrawableEntity)entity).BoundingRectangle;
                Rectangle bottomBounds = new Rectangle(entityRect.Left + collisionBoxExtraSize2, entityRect.Bottom, entityRect.Width - collisionBoxExtraSize1, 5);
                Rectangle rightBounds = new Rectangle(entityRect.Right, entityRect.Top + collisionBoxExtraSize2, 5, entityRect.Height - collisionBoxExtraSize1);
                Rectangle leftBounds = new Rectangle(entityRect.Left, entityRect.Top + collisionBoxExtraSize2, collisionBoxExtraSize2, entityRect.Height - collisionBoxExtraSize1);
                bool collidedBottom = IsCollidingWithMap(bottomBounds, deathTrapMap, color);
                bool collidedLeft = IsCollidingWithMap(leftBounds, deathTrapMap, color);
                bool collidedRight = IsCollidingWithMap(bottomBounds, deathTrapMap, color);

                if (collidedBottom)
                {
                    killable.DealDamage(110);
                }   
            }
        }

        public static int CollisionBoxExtraSize1
        {
            get { return collisionBoxExtraSize1; }
            set { collisionBoxExtraSize1 = value; }
        }

        public static int CollisionBoxExtraSize2
        {
            get { return collisionBoxExtraSize2; }
            set { collisionBoxExtraSize2 = value; }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="bounds">The bounds.</param>
        /// <param name="collisionMap">The collision map.</param>
        /// <param name="color">The color.</param>
        /// <returns></returns>
        public static bool IsCollidingWithMap(Rectangle bounds, Texture2D collisionMap, Color color)
        {
            Color[] data = new Color[bounds.Height * bounds.Width];

            if (bounds.X < 0 || bounds.Y < 0)
                return false;
            try
            {
                collisionMap.GetData<Color>(0, bounds, data, 0, data.Length);
            }
            catch (ArgumentException)
            {
                return false;
            }
            catch (NullReferenceException)
            {
                return false;
            }

            for (int i = 0; i < data.Length; i++)
            {
                if (data[i] == color)
                    return true;
            }

            return false;
        }

        public static void CheckCollisionMap(AbstractEntity entity, Texture2D collisionMap)
        {
            MoveableComponent moveable = (MoveableComponent)entity.GetComponent(typeof(MoveableComponent)); 
            if (moveable != null)
            {
                Rectangle entityRect = ((AbstractDrawableEntity)entity).BoundingRectangle;

                if (moveable.movementDirection == MoveableComponent.MoveDir.Forward)
                {
                    Rectangle bottomBounds = new Rectangle(entityRect.Left + collisionBoxExtraSize2, entityRect.Bottom, entityRect.Width - collisionBoxExtraSize1, collisionBoxExtraSize2);
                    Rectangle rightBounds = new Rectangle(entityRect.Right, entityRect.Top + collisionBoxExtraSize2, collisionBoxExtraSize2, entityRect.Height - collisionBoxExtraSize1);
                    Rectangle topBounds = new Rectangle(entityRect.Left + collisionBoxExtraSize2, entityRect.Top, entityRect.Width - collisionBoxExtraSize1, collisionBoxExtraSize2);
                    Rectangle leftBounds = new Rectangle(entityRect.Left, entityRect.Top + collisionBoxExtraSize2, collisionBoxExtraSize2, entityRect.Height - collisionBoxExtraSize1);

                    isBottom = IsCollidingWithMap(bottomBounds, collisionMap, Color.Red);
                    isRight = IsCollidingWithMap(rightBounds, collisionMap, Color.Red);
                    isTop = IsCollidingWithMap(topBounds, collisionMap, Color.Red);
                    isLeft = IsCollidingWithMap(leftBounds, collisionMap, Color.Red);

                    // Collide right
                    if (!isRight)
                        entity.position.X += moveable.xVelocity / gameLoopTime;
                    else
                        moveable.CollideWithEnvironment(MoveableComponent.MoveDir.Forward);

                    // Collide bottom
                    if (isBottom)
                        moveable.movementState = MoveableComponent.State.OnGround;
                    else
                        moveable.movementState = MoveableComponent.State.InAir;

                    if (isBottom && isLeft && isRight)
                    {
                        moveable.movementState = MoveableComponent.State.OnGround;
                        entity.position.Y -= 5f;
                    }

                    if (!isTop && !isBottom)
                        entity.position.Y += moveable.yVelocity / gameLoopTime;
                    else if (!(isTop) && moveable.yVelocity <= 0)
                        entity.position.Y += moveable.yVelocity / gameLoopTime;
                    else if (!(isBottom) && moveable.yVelocity >= 0)
                        entity.position.Y += moveable.yVelocity / gameLoopTime;
                }

                if (moveable.movementDirection == MoveableComponent.MoveDir.Backward)
                {
                    Rectangle bottomBounds = new Rectangle(entityRect.Left + collisionBoxExtraSize2, entityRect.Bottom, entityRect.Width - collisionBoxExtraSize1, collisionBoxExtraSize2);
                    Rectangle leftBounds = new Rectangle(entityRect.Left, entityRect.Top + collisionBoxExtraSize2, collisionBoxExtraSize2, entityRect.Height - collisionBoxExtraSize1);
                    Rectangle topBounds = new Rectangle(entityRect.Left + collisionBoxExtraSize2, entityRect.Top, entityRect.Width - collisionBoxExtraSize1, collisionBoxExtraSize2);
                    Rectangle rightBounds = new Rectangle(entityRect.Right, entityRect.Top + collisionBoxExtraSize2, collisionBoxExtraSize2, entityRect.Height - collisionBoxExtraSize1);
                    
                    isBottom = IsCollidingWithMap(bottomBounds, collisionMap, Color.Red);
                    isLeft = IsCollidingWithMap(leftBounds, collisionMap, Color.Red);
                    isTop = IsCollidingWithMap(topBounds, collisionMap, Color.Red);
                    isRight = IsCollidingWithMap(rightBounds, collisionMap, Color.Red);

                    // Collide left
                    if (!isLeft)
                        entity.position.X += moveable.xVelocity / gameLoopTime;
                    else
                        moveable.CollideWithEnvironment(MoveableComponent.MoveDir.Backward);

                    // Collide bottom
                    if (isBottom)
                        moveable.movementState = MoveableComponent.State.OnGround;
                    else
                        moveable.movementState = MoveableComponent.State.InAir;

                    if (isBottom && isLeft && isRight)
                    {
                        moveable.movementState = MoveableComponent.State.OnGround;
                        entity.position.Y -= 5f;
                    }

                    if (!isTop && !isBottom)
                        entity.position.Y += moveable.yVelocity / gameLoopTime;
                    else if (!(isTop) && moveable.yVelocity <= 0)
                        entity.position.Y += moveable.yVelocity / gameLoopTime;
                    else if (!(isBottom) && moveable.yVelocity >= 0)
                        entity.position.Y += moveable.yVelocity / gameLoopTime;
                }
            }
            else
                return;
        }


        public bool PixelPerfectCollision(AbstractEntity entity1, AbstractEntity entity2)
        {
            Color[] color1 = new Color[entity1.texture.Width * entity1.texture.Height];
            Color[] color2 = new Color[entity2.texture.Width * entity2.texture.Height];
            
            entity1.texture.GetData(color1);
            entity2.texture.GetData(color2);

            Rectangle intersection = Rectangle.Intersect(entity1.BoundingRectangle, entity2.BoundingRectangle);

            for (int y = intersection.Top; y < intersection.Bottom; y++)
            {
                for (int x = intersection.Left; x < intersection.Right; x++)
                {
                    Color colorA = color1[(x - entity1.BoundingRectangle.Left) +
                        (y - entity1.BoundingRectangle.Top) * entity1.BoundingRectangle.Height];

                    Color colorB = color1[(x - entity2.BoundingRectangle.Left) +
                        (y - entity2.BoundingRectangle.Top) * entity2.BoundingRectangle.Height];

                    if (colorA.A != 0 && colorB.A != 0)
                    {
                        return true;
                    }
                }
            }

            return false;


        }

        public static bool IsIntersectingPixels(AbstractEntity entity1, AbstractEntity entity2, int alphaThreshold1, int alphaThreshold2)
        {
            // Get the bounding rectangle of the person
            Rectangle entity1Rectangle =
                            new Rectangle((int)entity1.position.X, (int)entity1.position.Y,
                            entity1.texture.Width, entity1.texture.Height);

            // Get the bounding rectangle of this block
            Rectangle entity2Rectangle =
                            new Rectangle((int)entity2.position.X, (int)entity2.position.Y,
                            entity2.texture.Width, entity2.texture.Height);

            // The color data for the images; used for per pixel collision
            Color[] entity1TextureData = new Color[entity1.texture.Width * entity1.texture.Height];
            Color[] entity2TextureData = new Color[entity2.texture.Width * entity2.texture.Height];
            entity1.texture.GetData(entity1TextureData);
            entity2.texture.GetData(entity2TextureData);
            return IsIntersectingPixels(entity1Rectangle, entity2Rectangle, entity1TextureData, entity2TextureData, 0, 0);
        }

        public static bool IsIntersectingPixels(Rectangle entity1Rectangle, Rectangle entity2Rectangle, Color[] entity1TextureData, Color[] entity2TextureData, int alphaThreshold1, int alphaThreshold2)
        {
            // Find the bounds of the rectangle intersection
            int top = Math.Max(entity1Rectangle.Top, entity2Rectangle.Top);
            int bottom = Math.Min(entity1Rectangle.Bottom, entity2Rectangle.Bottom);
            int left = Math.Max(entity1Rectangle.Left, entity2Rectangle.Left);
            int right = Math.Min(entity1Rectangle.Right, entity2Rectangle.Right);

            // Check every point within the intersection bounds
            for (int y = top; y < bottom; y++)
            {
                for (int x = left; x < right; x++)
                {
                    // Get the color of both pixels at this point
                    Color colorA = entity1TextureData[(x - entity1Rectangle.Left) +
                                         (y - entity1Rectangle.Top) * entity1Rectangle.Width];
                    Color colorB = entity2TextureData[(x - entity2Rectangle.Left) +
                                         (y - entity2Rectangle.Top) * entity2Rectangle.Width];

                    // If both pixels are not completely transparent,
                    if (colorA.A > alphaThreshold1 && colorB.A > alphaThreshold2)
                    {
                        // then an intersection has been found
                        return true;
                    }
                }
            }

            return false;
        }

        public float calcGravityPull(float mass)
        {
            return UNIGRAVITY * mass;
        }

        private void PerformPhysics(out AbstractEntity entity)
        {
            entity = null;
        }
    }
}
